package com.xiaoxi.gateway.filter;

import com.xiaoxi.common.constant.enumeration.statuscode.system.TokenSystemEnums;
import com.xiaoxi.common.response.ApiResponse;
import com.xiaoxi.common.util.JwtTokenUtils;
import com.xiaoxi.domain.user.BMSUserContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.PatternMatchUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.Objects;

/**
 * @Author xiaoxi
 * @ProjectName springcloudbms
 * @PackageName com.gateway.filter
 * @ClassName MyGlobalFilter
 * @Description TODO
 * @Date 2023/2/1 10:30
 * @Version 1.0
 */
@Slf4j
@Component
public class MyGlobalFilter implements GlobalFilter {

    @Value("${release.requests}")
    private String[] whiteRequestUrl;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        log.info("当前环境已开启token校验");
        ServerHttpRequest request = exchange.getRequest();

        // 1. 放行白名单url
        boolean matchFlag = PatternMatchUtils.simpleMatch(whiteRequestUrl, request.getPath().toString());
        if (matchFlag) {
            log.info("接口已放行：{}", request.getPath());
            return chain.filter(exchange);
        }

        // 2. 验证token
        HttpHeaders headers = request.getHeaders();
        ServerHttpResponse response = exchange.getResponse();
        // 2.1 取Authorization
        String tokenHeader = headers.getFirst(HttpHeaders.AUTHORIZATION);
        // 2.1.1 如果token不存在
        if (StringUtils.isEmpty(tokenHeader)) {
            return returnResponse(ApiResponse.ofEnum(TokenSystemEnums.HEADER_AUTHORIZATION_NOT_CARRY), response);
        }

        // 2.1.2 如果token存在，取token
        String token = getToken(tokenHeader);
        log.info("token : {}", token);

        // 2.1.2.1 虽然 请求头 带上了 - Authorization，但是 前缀 不符合 规定
        if (StringUtils.isEmpty(token)) {
            log.error("token不存在");
            return returnResponse(ApiResponse.ofEnum(TokenSystemEnums.HEADER_AUTHORIZATION_FAIL), response);
        }

        // 2.1.2.2 校验 token是否失效
        BMSUserContext bmsUserContext = JwtTokenUtils.getUserToken(token);
        log.info("用户信息 : {}", bmsUserContext);
        if (Objects.isNull(bmsUserContext)) {
            log.error("token失效");
            return returnResponse(ApiResponse.ofEnum(TokenSystemEnums.TOKEN_INVALID), response);
        }

        // 2.1.2.3 校验 token 是否过期
        if (JwtTokenUtils.isTokenExpired(token)) {
            return returnResponse(ApiResponse.ofEnum(TokenSystemEnums.TOKEN_OVERDUE), response);
        }

        // 3. 鉴权-当前用户是否有访问url的权限


        // 4. 将token替换
        exchange.getRequest().mutate().header(HttpHeaders.AUTHORIZATION, token).build();

        return chain.filter(exchange);
    }

    /**
     * 解析Token
     */
    public String getToken(String requestHeader) {
        //2.Cookie中没有从header中获取
        String tokenHead = "Bearer";
        if (requestHeader.startsWith(tokenHead)) {
            return requestHeader.substring(tokenHead.length());
        }
        return "";
    }

    public Mono<Void> returnResponse(ApiResponse apiResponse, ServerHttpResponse response) {
        return response.writeWith(Flux.just(response.bufferFactory().wrap(Objects.requireNonNull(ApiResponse.apiResponseToJson(apiResponse)).getBytes(StandardCharsets.UTF_8))));
    }

}
